#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "calc.h"
#include "buf.h"

static OP mapop(char *s);
static bool getword(char *s);
static variables *var_malloc();

char name[NAME_LEN] = {0};
double value;

const char *getoperate(OP o);
int getop(char s[])
{
  int i, c;
  while ((s[0] = c = getch()) == ' ' || c == '\t') ;
  s[1] = '\0';
  if (c == '\n')
    return EQ;

  // is operator
  if (getword(s))
    return mapop(s);

  i = 0;
  if (isdigit(c) || c == '-')
    while (isdigit(s[++i] = c = getch())) ;
  if (c == '.')
    while (isdigit(s[++i] = c = getch())) ;
  s[i] = '\0';
  if (c != EOF)
    ungetch(c);
  return NUMBER;
}

static bool getword(char s[])
{
  char c;
  bool isoperator;
  int i = 0;
  if (*s == '\n' || *s == EOF) {
    isoperator = TRUE;
  } else if (isdigit(*s) || *s == '.') {
    isoperator = FALSE;
  } else if (*s == '-') {
    if (isdigit(c = getch()))
      isoperator = FALSE;
    else
      isoperator = TRUE;
    ungetch(c);
  } else {
    while ((s[++i] = c = getch()) != ' ' && c != '\n') ;
    ungetch(c);
    s[i] = '\0';
    isoperator = TRUE;
  }
  return isoperator;
}

static OP mapop(char s[])
{
  OP ret = ERR;
  int ischar = strlen(s) == 1;
  if (ischar) {
    switch (*s) {
      case '+':
        ret = PLUS;
        break;
      case '-':
        ret = MINUS;
        break;
      case '*':
        ret = MULTI;
        break;
      case '/':
        ret = DIVIDE;
        break;
      case '%':
        ret = MOD;
        break;
      case '\n':
        ret = EQ;
        break;
      case EOF:
        ret = QUIT;
        break;
    }
  } else {
    if (0 == strcmp(s, "sin"))
      ret = SIN;
    else if (0 == strcmp(s, "exp"))
      ret = EXP;
    else if (0 == strcmp(s, "pow"))
      ret = POW;
    else if (0 == strcmp(s, "set"))
      ret = SET;
  }
  if (ret == ERR) {
    /* if (0 == strcmp(name, s)) { */
    if (-1 != get_var(s)) {
      ret = VAR;
      printf("%s: ret = VAR\n", __func__);
    }
  }
  printf("%s: s, %s; ret %s\n", __func__, s, getoperate(ret));
  return ret;
}

double get_var(char *s)
{
  variables *p = vars;
  while (p && p->name) {
    if (strcmp(s, p->name) == 0)
      return p->value;
    p = p->next;
  }
  return -1;
}

void set_var(char *s, double d)
{
  if (!vars)
    vars = var_cur = var_malloc();
  else {
    var_cur->next = var_malloc();
    var_cur = var_cur->next;
  }
  strncpy(var_cur->name, s, NAME_LEN);
  var_cur->value = d;
  printf("%s set var %s: %g\n", __func__, s, d);
}
variables *var_malloc(void)
{
  variables *p;
  p = (variables*)malloc(sizeof(variables));
  memset(p, 0, sizeof(variables));
  p->name = (char*)malloc(NAME_LEN);
  memset(p->name, 0, NAME_LEN);
  return p;
}

void free_var(void)
{
  while (vars) {
    var_cur = vars;
    vars = vars->next;
    free(var_cur->name);
    free(var_cur);
  }
}

const char *getoperate(OP o)
{
  switch (o) {
    case ERR:
      return "ERR";
    case NUMBER:
      return "NUMBER";
    case PLUS:
      return "PLUS";
    case MINUS:
      return "MINUS";
    case MULTI:
      return "MULTI";
    case DIVIDE:
      return "DIVIDE";
    case MOD:
      return "MOD";
    case SIN:
      return "SIN";
    case EXP:
      return "EXP";
    case POW:
      return "POW";
    case EQ:
      return "EQ";
    case QUIT:
      return "QUIT";
    case SET:
      return "SET";
    case VAR:
      return "VAR";
  }
  return "NO OP";
}
